﻿using ServiceStack.Text;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StackExchange.Redis
{
    public static class TypedListExtensionMethods
    {
        public static TItem[] ListRange<TItem>(this IDatabase database, RedisKey key, long start = 0, long stop = -1, CommandFlags flags = CommandFlags.None)
        {
            var values = database.ListRange(key, start, stop, flags);
            if (values == null) return null;
            if (values.Length == 0) return new TItem[0];

            TItem[] items = new TItem[values.Length];
            for (int i = 0; i < values.Length; i++)
            {
                items[i] = JsonSerializer.DeserializeFromString<TItem>(values[i].ToString());
            }
            return items;
        }
        public static Task<TItem[]> ListRangeAsync<TItem>(this IDatabase database, RedisKey key, long start = 0, long stop = -1, CommandFlags flags = CommandFlags.None)
        {
            return database.ListRangeAsync(key, start, stop, flags).ContinueWith(task =>
            {
                var values = task.Result;
                if (values == null) return null;
                if (values.Length == 0) return new TItem[0];
                TItem[] items = new TItem[values.Length];
                for (int i = 0; i < values.Length; i++)
                {
                    items[i] = JsonSerializer.DeserializeFromString<TItem>(values[i].ToString());
                }
                return items;
            });
        }

        public static TItem ListGetByIndex<TItem>(this IDatabase database, RedisKey key, long index, CommandFlags flags = CommandFlags.None)
        {
            var value = database.ListGetByIndex(key, index, flags);
            if (value.HasValue)
            {
                var item = JsonSerializer.DeserializeFromString<TItem>(value.ToString());
                return item;
            }
            return default(TItem);
        }

        public static Task<TItem> ListGetByIndexAsync<TItem>(this IDatabase database, RedisKey key, long index, CommandFlags flags = CommandFlags.None)
        {
            return database.ListGetByIndexAsync(key, index, flags).ContinueWith(task =>
            {
                var value = task.Result;
                if (value.HasValue)
                {
                    var item = JsonSerializer.DeserializeFromString<TItem>(value.ToString());
                    return item;
                }
                return default(TItem);
            });
        }


        public static TItem ListLeftPop<TItem>(this IDatabase database, RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            var value = database.ListLeftPop(key, flags);
            if (value.HasValue)
            {
                var item = JsonSerializer.DeserializeFromString<TItem>(value.ToString());
                return item;
            }
            return default(TItem);
        }

        public static Task<TItem> ListLeftPopAsync<TItem>(this IDatabase database, RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            return database.ListLeftPopAsync(key, flags).ContinueWith(task =>
            {
                var value = task.Result;
                if (value.HasValue)
                {
                    var item = JsonSerializer.DeserializeFromString<TItem>(value.ToString());
                    return item;
                }
                return default(TItem);
            });
        }

        public static long ListLeftPush<TItem>(this IDatabase database, RedisKey key, TItem item, When when = When.Always, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            return database.ListLeftPush(key, value, when, flags);
        }

        public static long ListLeftPush<TItem>(this IDatabase database, RedisKey key, TItem[] items, CommandFlags flags = CommandFlags.None)
        {
            if (items == null || items.Length == 0) return 0;
            RedisValue[] values = new RedisValue[items.Length];
            for (int i = 0; i < items.Length; i++)
            {
                values[i] = JsonSerializer.SerializeToString(items[i]);
            }
            return database.ListLeftPush(key, values, flags);
        }

        public static Task<long> ListLeftPushAsync<TItem>(this IDatabase database, RedisKey key, TItem item, When when = When.Always, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            return database.ListLeftPushAsync(key, value, when, flags);
        }

        public static Task<long> ListLeftPushAsync<TItem>(this IDatabase database, RedisKey key, TItem[] items, CommandFlags flags = CommandFlags.None)
        {
            if (items == null || items.Length == 0) return new Task<long>(() => 0);
            RedisValue[] values = new RedisValue[items.Length];
            for (int i = 0; i < items.Length; i++)
            {
                values[i] = JsonSerializer.SerializeToString(items[i]);
            }
            return database.ListLeftPushAsync(key, values, flags);
        }


        public static long ListRemove<TItem>(this IDatabase database, RedisKey key, TItem item, long count = 0, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            return database.ListRemove(key, value, count, flags);
        }

        public static Task<long> ListRemoveAsync<TItem>(this IDatabase database, RedisKey key, TItem item, long count = 0, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            return database.ListRemoveAsync(key, value, count, flags);
        }

        public static TItem ListRightPop<TItem>(this IDatabase database, RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            var value = database.ListRightPop(key, flags);
            if (value.HasValue)
            {
                return JsonSerializer.DeserializeFromString<TItem>(value);
            }
            else
            {
                return default(TItem);
            }
        }

        public static Task<TItem> ListRightPopAsync<TItem>(this IDatabase database, RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            return database.ListRightPopAsync(key, flags).ContinueWith(task =>
            {
                var value = task.Result;
                if (value.HasValue)
                {
                    return JsonSerializer.DeserializeFromString<TItem>(value);
                }
                else
                {
                    return default(TItem);
                }
            });
        }


        public static TItem ListRightPopLeftPush<TItem>(this IDatabase database, RedisKey source, RedisKey destination, CommandFlags flags = CommandFlags.None)
        {
            var value = database.ListRightPopLeftPush(source, destination, flags);
            if (value.HasValue)
            {
                return JsonSerializer.DeserializeFromString<TItem>(value);
            }
            else
            {
                return default(TItem);
            }
        }

        public static Task<TItem> ListRightPopLeftPushAsync<TItem>(this IDatabase database, RedisKey source, RedisKey destination, CommandFlags flags = CommandFlags.None)
        {
            return database.ListRightPopLeftPushAsync(source, destination, flags).ContinueWith(task =>
            {
                var value = task.Result;
                if (value.HasValue)
                {
                    return JsonSerializer.DeserializeFromString<TItem>(value);
                }
                else
                {
                    return default(TItem);
                }
            });
        }

        public static long ListRightPush<TItem>(this IDatabase database, RedisKey key, TItem item, When when = When.Always, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            return database.ListRightPush(key, value, when, flags);
        }

        public static Task<long> ListRightPushAsync<TItem>(this IDatabase database, RedisKey key, TItem item, When when = When.Always, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            return database.ListRightPushAsync(key, value, when, flags);
        }

        public static long ListRightPush<TItem>(this IDatabase database, RedisKey key, TItem[] items, CommandFlags flags = CommandFlags.None)
        {
            if (items == null || items.Length == 0) return 0;
            RedisValue[] values = new RedisValue[items.Length];
            for (int i = 0; i < items.Length; i++)
            {
                values[i] = JsonSerializer.SerializeToString(items[i]);
            }
            return database.ListRightPush(key, values, flags);
        }

        public static Task<long> ListRightPushAsync<TItem>(this IDatabase database, RedisKey key, TItem[] items, CommandFlags flags = CommandFlags.None)
        {
            if (items == null || items.Length == 0) return new Task<long>(() => 0);
            RedisValue[] values = new RedisValue[items.Length];
            for (int i = 0; i < items.Length; i++)
            {
                values[i] = JsonSerializer.SerializeToString(items[i]);
            }
            return database.ListRightPushAsync(key, values, flags);
        }

        public static void ListSetByIndex<TItem>(this IDatabase database, RedisKey key, long index, TItem item, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            database.ListSetByIndex(key, index, value, flags);
        }

        public static Task ListSetByIndexAsync<TItem>(this IDatabase database, RedisKey key, long index, TItem item, CommandFlags flags = CommandFlags.None)
        {
            var value = JsonSerializer.SerializeToString(item);
            return database.ListSetByIndexAsync(key, index, value, flags);
        }
    }
}
